/*
 * JBeagle - a Java toolkit for genetic algorithms.
 * 
 * Copyright (c) 2010 Matthijs Snel
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package jbeagle.core;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Iterator;
import java.util.ListIterator;

public class ListIndividual<E> extends AbstractIndividual<E> 
								implements List<E>, Cloneable {

	private static final long serialVersionUID = -3664346193802383937L;
	
	protected ArrayList<E> genes;
	
	public ListIndividual() {
		super();
        genes = new ArrayList<E>();
	}

	public ListIndividual(Collection<? extends E> c) {
		super();
		genes = new ArrayList<E>(c);
	}

	public ListIndividual(int length) {
		super();
		genes = new ArrayList<E>(length);
	}

        @Override
        public void add(int index, E e) {
            if( constraints != null && constraints.violates(e) )
			throw new ConstraintViolatedException(e.toString());
                
            safeAdd(index, e);
        }
                
        @Override        
	public boolean addAll(int index, Collection<? extends E> c) {
		if( constraints != null ) {
                    E e = constraints.violates(c);
                    if( e != null )
                        throw new ConstraintViolatedException(e.toString());
                }
                
                return safeAddAll(index, c);
	}
	
         @Override
	public void clear() {
		genes.clear();
		setFitness(0);
	}
         
    @Override
	public Object clone() {
		return copy();
	}
	
         @Override
	public boolean contains(Object o) {
		return genes.contains(o);
	}
	
         @Override
	public boolean containsAll(Collection<?> c) {
		return genes.containsAll(c);
	}
	
    public Individual<E> copy() {
    	ListIndividual<E> ind = new ListIndividual<E>( genes );
		ind.setFitness( fitness );
		return ind;
    }
    
	/**
     * Compares the specified object with this individual for equality.  Returns
     * {@code true} if and only if the specified object is also a {@code ListIndividual}, both
     * individuals have the same size, and all corresponding pairs of elements in
     * the two individuals are <i>equal</i>.  (Two elements {@code e1} and
     * {@code e2} are <i>equal</i> if {@code (e1==null ? e2==null :
     * e1.equals(e2))}.)  In other words, two individuals are defined to be
     * equal if they contain the same elements in the same order.<p>
     *
     * This implementation first checks if the specified object is a ListIndividual.
     * If not, it returns {@code false}; if so, it checks whether it has the same size
     * as this individual.  
     * 
     * If the size of the individuals is not the same, this method returns false.
     * If not, it iterates over both individuals, comparing corresponding pairs of elements.
     * If any comparison returns {@code false}, this method returns
     * {@code false}. It returns {@code true} when the iterations complete.
     * 
     * NOTE: this implementation of equals results in a natural ordering that is
     * inconsistent with equals: the natural ordering (using the {@link #compareTo(Individual)}
     * method is based on fitness, but {@link #equals(Object)} does not take fitness
     * into account. Therefore, constructing a {@code SortedSet} of {@code Individual}s
     * does not make sense (unless a custom {@code Comparator} is provided, of course).
     *
     * @param o the object to be compared for equality with this ListIndividual
     * @return {@code true} if the specified object is equal to this ListIndividual
     */
	 @SuppressWarnings("unchecked")
	@Override
        public boolean equals(Object o) {
		if( !(o instanceof ListIndividual) )
			return false;
		
		//Check for size equality before invoking genes.equals in order to avoid iterating
		//over whole list even if size is unequal
		if( ((List) o).size() != size() )
			return false;
		
		return genes.equals(o);
	}
	
         @Override
	public E get(int index) {
		return genes.get(index);
	}
	
         @Override
	public int hashCode() {
		return (int) (31*genes.hashCode() + Math.round((10^precision)*fitness));
	}
	
         @Override
	public int indexOf(Object o) {
		return genes.indexOf(o);
	}
	
         @Override
	public boolean isEmpty() {
		return genes.isEmpty();
	}
	
         @Override
	public Iterator<E> iterator() {
		return genes.iterator();
	}
	
         @Override
	public int lastIndexOf(Object o) {
		return genes.lastIndexOf(o);
	}
	
         @Override
	public ListIterator<E> listIterator() {
		return genes.listIterator();
	}
	
         @Override
	public ListIterator<E> listIterator(int index) {
		return genes.listIterator(index);
	}
	
         @Override
	public E remove(int index) {
		return genes.remove(index);
	}
	
        @Override
	public boolean remove(Object o) {
		return genes.remove(o);
	}
	
        @Override
	public boolean removeAll(Collection<?> c) {
		return genes.removeAll(c);
	}
	
	public void removeRange(int fromIndex, int toIndex) {
		//cheaper to keep other elements if range is large
		/*if ( (toIndex - fromIndex - 5) > size() / 3 * 2 ) {
			List<E> l1 = subList(0, fromIndex),
					l2 = subList(toIndex, size());
			clear();
			addAll(l1); addAll(l2);
		} else {*/
			for( int i = 0; i < (toIndex - fromIndex); i++) {
				remove(fromIndex);
			}
		//}
	}
	
        @Override
	public boolean retainAll(Collection<?> c) {
		return genes.retainAll(c);
	}
	
        @Override
	public E set(int index, E e) {
		if( constraints != null && constraints.violates(e) )
			throw new ConstraintViolatedException(e.toString());
                
		return genes.set(index, e);
	}
	
        @Override
	public int size() {
		return genes.size();
	}
	
        @Override
	public List<E> subList(int fromIndex, int toIndex) {
		return genes.subList(fromIndex, toIndex);
	}
	
        @Override
	public Object[] toArray() {
		return genes.toArray();
	}
	
        @Override
	public <T> T[] toArray(T[] a) {
		return genes.toArray(a);
	}
        
        @Override
        protected boolean safeAdd(E e) {
		return genes.add(e);
	}
	
        protected void safeAdd(int index, E e) {
            genes.add(index, e);
        }
        
        @Override
	protected boolean safeAddAll(Collection<? extends E> c) {
		return genes.addAll(c);
	}
        
        protected boolean safeAddAll(int index, Collection<? extends E> c) {
            return genes.addAll(index, c);
        }
}
